home *** CD-ROM | disk | FTP | other *** search
- /*
- Startup-code for BareED
- Written 10 & 11/99 by Jörg van de Loo, Hövel 15, 47559 Kranenburg, FRG.
-
- This startup-code is freeware and it's especially written in mind the
- MaxonC++ compiler V4 (useful for C and ANSI-C modes only).
-
- CLI parser 'borrowed' from AZTEC-C package (modified).
-
- This startup codes sets up argc, argv with the commandline parameters
- given through the CLI and with all selected files given through
- Workbench, e.g. double click on project icon or shift select. Additional,
- it ensures the present of a 68020 processor and Kickstart 2.0 (beta).
-
- No stdin, stdout, stderr terminals will be set up since they are useless
- for BareED.
-
- SysBase and ExecBase are only present within this startup-code; this
- ensures that the additional code of BareED doesn't refer to the variables
- set up by the startup-code. This is due to the fact that my compiler
- would otherwise address them as 32 bit addresses, that will cause each
- time a reloc32 hunk entry. What I do is to refer to _DOS_Base and
- _Exec_Base once, to set up DOSBase and SysBase in the additional code of
- BareED, so that my compiler will them address from now on within BareED
- indirect to the processor register a4.
-
- When you compile this startup-code ensure that no 68020 or higher
- instructions are used before the processor check is executed, otherwise
- on a plain 68000 processor this startup-code will fail with a GURU.
-
- This startup-code checks for the amount of free stack, too. Since this
- startup-code uses very less stack (56 bytes when running and only up to
- 328 bytes while setting up variables) it should be enought when using a
- stack size of only 4096 bytes for the loadfile of BareED, even under 3rd
- party graphic emulation systems - which in fact do require much more
- stack than the native Amiga OS 3 graphic system (up to 3Kb when BareED is
- running).
-
- One goal of this startup-code is, that it gains at lot of stack (up to
- 1.1 Kb) compared to the original startup-code that comes along with my
- compiler.
- Currently it needs a given stack size of 4052 bytes when started off a CLI
- surround and 4046 bytes when started off the Workbench.
-
- 11/99 - modified this startup-code to run-back when started off a CLI
- window or called with RunCommand() or within a batch script of an
- application.
-
- If you are able to adapt this startup-code to your compiler please do me
- a favour and return the adapted startup-code for free to me, in this case
- e-mail Marc Berson at:
- BersonM@aol.com
- and state clearly that the message is for me and concerned BareED.
-
- NOTE: startup.c/BareED.c can be only compiled under GNU-C in large mode!
- This is due to a bug in GNU-C; spilled registers.
- I used the GNU-Compiler that can be found on Geek Gadgets II.
- Thanks to Mr. Fish for this compilation.
- */
-
-
- #include <exec/execbase.h>
- #include <exec/libraries.h>
- #include <exec/memory.h>
-
- #include <dos/dosextens.h>
- #include <dos/dostags.h>
-
- #include <utility/tagitem.h>
-
- #include <clib/exec_protos.h>
- #include <clib/dos_protos.h>
-
- #ifdef __GNUC__
- #include <pragmas/exec_pragmas.h>
- #include <pragmas/dos_pragmas.h>
- #else
- #include <pragma/exec_lib.h>
- #include <pragma/dos_lib.h>
- #endif
-
- #include <dos/dosextens.h>
-
- #include <workbench/startup.h>
-
- struct ExecBase *_Exec_Base;
- struct DosLibrary *_DOS_Base;
- struct Message *WBenchMsg;
-
- #ifdef __MAXON__
- #ifndef __cplusplus
- static struct ExecBase *SysBase; // Allow only to appear within startup.c
- static struct DosLibrary *DOSBase; // Allow only to appear within startup.c
- #else
- extern struct ExecBase *SysBase;
- extern struct DosLibrary *DOSBase;
- #endif
- #endif
-
- #ifndef __MAXON__
- extern struct ExecBase *SysBase;
- extern struct DOSLibrary *DOSBase;
- #endif
-
-
- static BPTR _prg_dir; // Remembered start dir
- static unsigned int _argc, _arg_len;
- static unsigned char **_argv, *_arg_lin;
-
- #ifdef __MAXON__
- extern "C" void GetBaseReg( void);
- #define geta4() GetBaseReg()
- #else
- extern geta4();
- #endif
-
- struct LaunchStuff
- {
- int nextHunk;
- int hunkSize;
- char name[128];
- int stack;
- int pri;
- int dummy; // To make this table divisible by 8
- };
-
- struct LaunchStuff *LS = 0;
- struct Process *MasterTask;
-
- unsigned char DOSName[] = "dos.library";
- static unsigned char Console[] = "CON:0/0/320/80/ERROR REPORT/AUTO/CLOSE/WAIT";
- static unsigned char NotEnoughtMem[]= "ERROR: Not enought memory!\n";
- static unsigned char NotEnoughtStack[]= "ERROR: Stack < 4096 bytes!\n";
- static unsigned char WrongCPU[] = "ERROR: CPU < 68020!\n";
- static unsigned char WrongLIB[] = "ERROR: Library versions < 36!\n";
- static unsigned char WrongSYS[] = "ERROR: System version < 36!\n";
- static unsigned char WrongError[] = "ERROR: Non describt fault!\n"; // ????
-
-
- void _main_jmp( unsigned long parlen, unsigned char *parameter);
- int main( unsigned long argc, unsigned char **argv);
-
-
- // ################################################################
-
- #ifdef __GNUC__
- void INIT_0_run_me_at_first_place( unsigned long parlen __asm("d0"), unsigned char *parameter __asm("a0") )
- #else
- void INIT_0_run_me_at_first_place( register __d0 unsigned long parlen, register __a0 unsigned char *parameter)
- #endif
- {
- #ifndef __GNUC__ // Since GNU-C does not allow to compile BareED as base relative code
- geta4();
- #endif
-
- _Exec_Base = SysBase = *((struct ExecBase **) 4); // From memory location 4 to exec library
- if (_Exec_Base -> LibNode . lib_Version <= 32) // Kickstart lower than 33 (OS1.2) = die
- return; // -> perhaps with a GURU (when started from WB)
-
- _main_jmp( parlen, parameter); // We never return! We jump to _main_jmp because if
- // we would do here the following stuff, d0 and a0
- // would be ever pre-reserved, which blows up the
- // objectfile un-necessary
- }
-
- // ################################################################
-
- /* Following code is to avoid reloc32 entries - since they would otherwise
- (when linked with the objectfiles) called via "jsr" and not via "bsr" */
-
- static unsigned int strlenNR( register const unsigned char *str)
- {
- register unsigned int i = 0;
-
- while (*str++)
- i++;
- return i;
- }
-
- static void strncpyNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
- {
- while (i)
- {
- *d++ = *s++;
- i--;
- }
- *d = 0;
- }
-
- static void strcpyNR( register unsigned char *d, register const unsigned char *s)
- {
- while (*s)
- *d++ = *s++;
- *d = 0;
- }
-
- /* static void strcatNR( register unsigned char *d, register const unsigned char *s)
- {
- while (*d)
- *d++;
-
- while( *s)
- *d++ = *s++;
- *d = 0;
- } */
-
- static void strncatNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
- {
- while (*d)
- *d++;
-
- while( i)
- {
- *d++ = *s++;
- i--;
- }
- *d = 0;
- }
-
- // ################################################################
-
- /* Function required by GNU-C linker, not necessary anymore - since
- all work already done by this startup-code! */
-
- #ifdef __GNUC__
- void __main( int argc, char **argv)
- {
- return;
- }
- #endif
-
- // ################################################################
-
- /* Taken without permission from the AZTEC-package, which in fact
- can be found in books and magazins, too */
-
- static void _cli_parse(struct Process *pp, unsigned long alen, register unsigned char *aptr)
- {
- register unsigned char *cp;
- register struct CommandLineInterface *cli;
- register unsigned char c;
-
- cli = (struct CommandLineInterface *) ( (unsigned long) pp->pr_CLI << 2);
- cp = (unsigned char *) ( (unsigned long) cli->cli_CommandName << 2);
-
- _arg_len = (unsigned char) cp[0] + alen + 2;
-
- if ( (_arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), (0x10000) ) ) == 0)
- return;
-
- c = cp[0];
- strncpyNR( _arg_lin, cp + 1, c);
- _arg_lin[c] = ' ';
- _arg_lin[c + 1] = 0;
- strncatNR( _arg_lin, aptr, alen);
- _arg_lin[c] = 0;
-
- for (_argc = 1, aptr = cp = _arg_lin + c + 1; ; _argc++)
- {
- while ( (c = *cp) == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n')
- cp++;
- if (*cp < ' ')
- break;
- if (*cp == '"')
- {
- cp++;
- while ( (c = *cp++) )
- {
- *aptr++ = c;
- if (c == '"')
- {
- if (*cp == '"')
- {
- cp++;
- }
- else
- {
- aptr[-1] = 0;
- break;
- }
- }
- }
- }
- else
- {
- while ( (c = *cp++) && c != ' ' && c != '\t' && c != '\f' && c != '\r' && c != '\n')
- *aptr++ = c;
- *aptr++ = 0;
- }
- if (c == 0)
- --cp;
- }
-
- *aptr = 0;
- if ( (_argv = (unsigned char **) AllocMem( (((_argc + 1) * 4 + 7) & -8 ), (0x10000) ) ) == 0 )
- {
- _argc = 0;
- return;
- }
-
- for (c=0, cp=_arg_lin; c < _argc; c++)
- {
- _argv[c] = cp;
- cp += strlenNR( cp) + 1;
- }
-
- _argv[c] = 0;
- }
-
- // ################################################################
-
- /* Allows several Workbench passed in arguments for BareED */
-
- static void _wb_parse( struct WBStartup *msg)
- {
- int numargs, len, i;
- char str[256], *curr; // Normally I'm against arrays on stack, but since we're
- // at front of a loadfile it doesn't matter (because there
- // is enough free stack available) and we can here avoid
- // memory fragmentation through AllocMem()
-
- numargs = msg -> sm_NumArgs;
- _arg_len = (numargs + 2) * 4; // Number of arguments into amount bytes (plus 2 longwords)
-
-
- /* Get length in bytes of all arguments including zero bytes and drawer terminators */
- i = 0;
- while (i < numargs)
- {
- if (msg -> sm_ArgList[i] . wa_Lock)
- NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
- _arg_len += strlenNR( &str[0]);
- _arg_len += 2; // For zero byte and perhaps for drawer terminator "/" !
- _arg_len += strlenNR( msg -> sm_ArgList[i] . wa_Name);
- i++;
- }
-
- /* Allocate needed space for strings */
- _arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), (0x10000) );
- if ( !_arg_lin)
- return;
-
- _argc = numargs;
- _argv = (unsigned char **) _arg_lin;
- curr = _arg_lin + ((numargs + 2) * 4);
-
- /* Create and copy drawer and filenames into allocated memory, behind the argument pointers! */
- i = 0;
- while (i < numargs)
- {
- _argv[i] = curr;
- if (msg -> sm_ArgList[i] . wa_Lock)
- {
- NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
- strcpyNR( curr, &str[0]);
- len = strlenNR( curr);
-
- if ( curr[ len - 1] != ':')
- {
- curr[len] = '/';
- len ++;
- }
- }
- else
- {
- len = 0;
- }
-
- strcpyNR( curr + len, msg -> sm_ArgList[i] . wa_Name);
- len = strlenNR( curr);
-
- curr += len + 1; // Behind zero byte
- i++; // Next arg
- }
-
- _prg_dir = CurrentDir( msg -> sm_ArgList[0] . wa_Lock); // Set up "progdir:"
- }
-
-
- // ################################################################
-
- /* Print error code down to a console window, if there isn't one yet,
- open one and give the message. */
-
- static void GiveFault( int error)
- {
- BPTR newStdOut;
- unsigned char *errorTxt;
-
-
- if (_DOS_Base) // DOSBase set up?
- {
- errorTxt = 0;
-
- if (_Exec_Base -> LibNode . lib_Version <= 35) // OS 2.0
- Console[27] = 0; // No, OS 1.x, so remove AUTO/CLOSE/WAIT from console description
-
- if (WBenchMsg) // If WB-start, open console window
- {
- newStdOut = Open( Console, MODE_OLDFILE);
- }
- else
- {
- newStdOut = Output();
- }
-
- if (newStdOut)
- {
- if (error == 236)
- errorTxt = WrongSYS;
- if (error == 105)
- errorTxt = WrongCPU;
- if (error == 122)
- errorTxt = WrongLIB;
- if (error == 217)
- errorTxt = NotEnoughtStack;
- if (error == 103)
- errorTxt = NotEnoughtMem;
- if (errorTxt == 0)
- errorTxt = WrongError; // ????
-
- Write( newStdOut, errorTxt, strlenNR( errorTxt) );
-
- if (WBenchMsg && _Exec_Base -> LibNode . lib_Version <= 35) // If WB-start and OS 1.x
- {
- Delay( 5*60); // Wait a while
- Close( newStdOut); // Close console
- }
- }
- }
- }
-
- // ################################################################
-
- /* Un-load startup-code and additional program code/data and bss.
- Free also the table we used, but restore the segment of our
- program first!
- This is only called if we are in 'RunBack' mode. */
-
- static void LaunchExit( void)
- {
- unsigned int *adr;
-
- adr = (unsigned int *) &(INIT_0_run_me_at_first_place);
- adr --;
- *adr = LS -> nextHunk; // Restore the link to the additional hunks
- adr --;
- *adr = LS -> hunkSize; // Restore first hunk's size (was set zero)
-
- FreeMem( LS, sizeof( struct LaunchStuff));
-
- Forbid(); // Must be called because we may not removed from memory since additional code must be run first!
- adr += 1; // Point back to segment entry for DOS
- UnLoadSeg( (BPTR) ((unsigned long) adr/4) );
- }
-
- // ################################################################
-
- /* Code to get program's return address and to call it
- That's the 'additional code that must be run first' */
-
- static const unsigned short _finally_code[] =
- {
- 0x2400, // move.l D0,D2 Save error code
- 0x93C9, // suba.l A1,A1
- 0x2C78,0x0004, // movea.l (4).w,A6
- 0x4EAE,0xFEDA, // jsr _LVOFindTask(A6) Own Task
- 0x2040, // movea.l D0,A0
- 0x2068,0x00B0, // movea.l pr_ReturnAddr(A0),A0 Get system's exit address for us
- 0x4FE8,0xFFFC, // lea -4(A0),sp Restore initial stack
- 0x2002, // move.l D2,D0 Error code to D0
- 0x4E75 // rts Back to system
- };
-
- // ################################################################
-
- /* Since my compiler doesn't allow to overload a function (stub), I use
- a different name: exitNR ! */
-
- void exitNR( int error)
- {
- #ifdef __GNUC__
- void (* _finally)( int err __asm("d0") );
- #else
- void (* _finally)( register __d0 int err);
- #endif
-
- geta4();
- (void *) _finally = (void *) _finally_code;
-
- if (error)
- GiveFault( error);
-
- ((struct Process *) _Exec_Base ->ThisTask) -> pr_Result2 = error;
-
- if (WBenchMsg)
- {
- if (_arg_lin)
- {
- FreeMem( _arg_lin, ((_arg_len + 7) & -8) );
- if (_prg_dir) // If it is zero (SYS:) ignore
- CurrentDir( _prg_dir); // Lock of basic directory
- }
- Forbid();
- ReplyMsg( WBenchMsg);
- }
- else
- {
- if (_arg_lin)
- {
- FreeMem(_argv, (((_argc + 1) * 4 + 7) & -8) );
- FreeMem(_arg_lin, ((_arg_len + 7) & -8) );
- }
- }
-
- if (_DOS_Base)
- CloseLibrary( (struct Library *) _DOS_Base);
-
- // Check if our application did a detach from a CLI
- if (LS)
- LaunchExit();
-
- _finally( error);
- }
-
- // ################################################################
-
- /* This function does not return to the caller ! */
-
- static void CallMainPRG( void)
- {
- exitNR( main( _argc, _argv));
- }
-
- // ################################################################
-
- /* If we are in 'RunBack' mode we call the main() function. The code
- below is executed as first part of the new created process. */
-
- static void LaunchMain( void)
- {
- geta4();
- Signal( (struct Task *) MasterTask, (0x10000));
- CallMainPRG();
- }
-
- // ################################################################
-
- /* Set up necessary thing to provide an auto-detach (RunBack mode).
- This is a little tricky one which cares about the priority and
- stack size of the original process which are the settings for
- the new created.
- */
-
- static void LaunchStartup( void)
- {
- struct MsgPort *msgp;
- struct Process *proc;
- unsigned int *adr;
- struct TagItem tag[5];
- struct CommandLineInterface *cli;
- unsigned char *cp;
-
-
- MasterTask = ((struct Process *) _Exec_Base -> ThisTask);
- LS = (struct LaunchStuff *) AllocMem( sizeof( struct LaunchStuff), MEMF_CLEAR|MEMF_PUBLIC);
- if (!LS) // No table?
- CallMainPRG(); // We never return!
-
- cli = (struct CommandLineInterface *) ( (unsigned long) MasterTask->pr_CLI << 2);
- cp = (unsigned char *) ( (unsigned long) cli->cli_CommandName << 2);
-
- // Duplicate name: slave task's name equal to master's one
- strncpyNR( LS->name, cp + 1, cp[0]);
- // Duplicate priority
- LS->pri = MasterTask -> pr_Task . tc_Node . ln_Pri;
- // Duplicate stack size
- LS->stack = (int) MasterTask->pr_Task.tc_SPUpper - (int) MasterTask->pr_Task.tc_SPLower;
-
- // Settings for task to create
- tag[0].ti_Tag = NP_Entry;
- tag[0].ti_Data = (ULONG) &(LaunchMain);
- tag[1].ti_Tag = NP_Name;
- tag[1].ti_Data = (ULONG) LS->name;
- tag[2].ti_Tag = NP_StackSize;
- tag[2].ti_Data = LS->stack;
- tag[3].ti_Tag = NP_Priority;
- tag[3].ti_Data = LS->pri;
- tag[4].ti_Tag = TAG_DONE;
-
- /* An AmigaDOS 'Hunk' looks like this (in memory)
- -4 int HunkSize
- 0 BPTR NextHunk <- The segment starts here (as BPTR), see LoadSeg(), UnLoadSeg(), etc.
- +4 Code/Data/BSS
- */
-
- // Create slave task which becomes master task
- msgp = (struct MsgPort *) CreateNewProc( tag);
- if (!msgp) // Failed to create task?
- {
- FreeMem( LS, sizeof( struct LaunchStuff));
- LS = 0;
- CallMainPRG(); // We never return!
- }
-
- // From message port of process (task) to process itself (slave)
- proc = (struct Process *) ((ULONG) msgp - sizeof( struct Task));
- Wait( (0x10000)); // Wait for slave task to signal this bit so that we can quit savely
-
- // First instruction of this startup-code is at location:
- adr = (unsigned int *) &(INIT_0_run_me_at_first_place);
-
- // Point to BPTR next hunk (seglist start)
- adr --;
- // Remember the hunk address
- LS -> nextHunk = *adr;
- // Break the hunk list of this loadfile
- *adr = 0;
-
- // Point to size of 'this' hunk (first hunk of loadfile)
- adr --;
- // Remember hunk size (normally code size, it's the HUNK_CODE)
- LS -> hunkSize = *adr;
- // This hunk size forced to zero
- *adr = 0;
-
- /* Why I did the things above?
- Remember ever that 2 tasks are running which share the same memory and instructions!
-
- If we would now quit the first by the system (user) created task, the memory and
- therewith the instructions are gone, too. Thus a crash isn't far. What I do is to
- tell AmigaDOS that the first created process has got only a single hunk with a size
- of zero (ok., I know I'm a liar) so that AmigaDOS doesn't frees anything when the
- first created task quits. The segment (hunk-list) is restored by function
- LaunchExit() and the memory used by it is removed by a call to AmigaDOS function
- UnLoadSeg(). Since UnLoadSeg() instantly gives the memory back to the system I
- forbid task switching and therwith memory allocation while the 2nd created task is
- staying alive. If it dies, the task switching is enabled automatically through the
- system.
- */
- }
-
- // ################################################################
-
- void _main_jmp( unsigned long parlen, unsigned char *parameter)
- {
- int error;
- struct Process *proc;
-
-
- error = 0;
- proc = ((struct Process *) _Exec_Base -> ThisTask);
-
- if (proc -> pr_CLI)
- {
- WBenchMsg = 0;
- }
- else
- {
- WaitPort( &proc -> pr_MsgPort);
- WBenchMsg = GetMsg( &proc -> pr_MsgPort);
- }
-
- if (_Exec_Base -> LibNode . lib_Version <= 35) // Lower Kickstart 2.0 (beta)?
- error = 236;
-
- _DOS_Base = DOSBase = (struct DosLibrary *) OpenLibrary( DOSName, 33);
- if ( !_DOS_Base || _DOS_Base -> dl_lib . lib_Version <= 35)
- error = 122;
-
- // If we have a soft-kicked 2.0 ROM for 1.x machines we raise at least an Exec error (236)
- if (error)
- exitNR( error);
-
- if ( !(_Exec_Base -> AttnFlags & AFF_68020)) // At least a 68020 processor?
- exitNR( 105);
-
-
- // Do we have at least 4000 bytes of stack available? - NOTE: the variable proc is on
- // the top of the stack so using this address is the current upper bound of the stack!
- if ( (((int) proc->pr_Task.tc_SPUpper - (int) proc->pr_Task.tc_SPLower) - ( (int) proc->pr_Task.tc_SPUpper - (int) &proc) ) < 4000 )
- exitNR( 217);
-
- if (WBenchMsg)
- {
- _wb_parse( (struct WBStartup *) WBenchMsg);
- if ( !_arg_lin)
- exitNR( 103); // Fail: no mem
- }
- else
- {
- _cli_parse( proc, parlen, parameter);
- if ( !_arg_lin)
- exitNR( 103);
- }
-
- if (WBenchMsg)
- {
- CallMainPRG();
- }
- else
- {
- LaunchStartup(); // Try detach from CLI, if it fails, go on normal
- }
- }
-
- // ################################################################
-